package com.gzhryc.servlet;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gzhryc.servlet.annotation.Route;
import com.gzhryc.servlet.resources.ErrorResource;
import com.gzhryc.servlet.resources.JsonResource;
import com.gzhryc.servlet.resources.TextResource;
import com.gzhryc.servlet.resources.VelocityContentResource;
import com.gzhryc.servlet.resources.VelocityResource;
import com.gzhryc.servlet.resources.VoidResource;
import com.gzhryc.servlet.upload.UploadFiles;

public class HttpHandler {
	
	Logger log = LoggerFactory.getLogger(this.getClass());
	
	private final Route route;
	private final Method method; // 处理方法
	private final Object obj; 	// 处理对象
	private Type[] types;
	
	private HttpHandler nextHandler;
	
	private Pattern[] patterns;
	private List<List<String>> names;
	
	public HttpHandler(Route route,Object obj,Method method){
		this.route = route;
		this.obj = obj;
		this.method = method;
		//获取方法返回类型
		this.types = this.method.getGenericParameterTypes();
		
		this.patterns = new Pattern[this.route.value().length];
		this.names = new ArrayList<List<String>>();
		for(int i=0;i<this.route.value().length;i++){
			String uriPattern = this.route.value()[i];
			
			Pattern pat = Pattern.compile("\\{([^\\}]+)\\}");
			Matcher mat = pat.matcher(uriPattern);
			List<String> temp = new ArrayList<String>();
			
			while(mat.find()){
				temp.add(mat.group(1));
				uriPattern = uriPattern.replace("{"+mat.group(1)+"}", "([^/]+)");
				mat = pat.matcher(uriPattern);
			}
			patterns[i] = Pattern.compile("^"+uriPattern+"$");
			names.add(temp);
		}
	}
	
	public HttpHandler(String contextPath,Route route,Object obj,Method method){
		this.route = route;
		this.obj = obj;
		this.method = method;
		//获取方法返回类型
		this.types = this.method.getGenericParameterTypes();
		
		this.patterns = new Pattern[this.route.value().length];
		this.names = new ArrayList<List<String>>();
		for(int i=0;i<this.route.value().length;i++){
			String uriPattern = contextPath + this.route.value()[i];
			
			Pattern pat = Pattern.compile("\\{([^\\}]+)\\}");
			Matcher mat = pat.matcher(uriPattern);
			List<String> temp = new ArrayList<String>();
			
			while(mat.find()){
				temp.add(mat.group(1));
				uriPattern = uriPattern.replace("{"+mat.group(1)+"}", "([^/]+)");
				mat = pat.matcher(uriPattern);
			}
			patterns[i] = Pattern.compile("^"+uriPattern+"$");
			names.add(temp);
		}
	}
	
	public boolean before(HttpServletRequest request,String uri){
		for(int i=0;i<patterns.length;i++){
			Matcher mat = patterns[i].matcher(uri);
			if(mat.find()){
				boolean flag = true;
				if(route.method().length > 0){
					flag = false;
					for(int j=0;j<route.method().length;j++){
						if(route.method()[j].toUpperCase().equals(request.getMethod())){
							flag = true;
							break;
						}
					}
				}
				
				if(flag){
					List<String> temp = names.get(i);
					if(temp.size() > 0){
						for(int j=0;j<temp.size();j++){
							request.setAttribute(temp.get(j),mat.group((j+1)));
						}
					}
					return flag;
				}
			}
		}
		return false;
	}
	
	public Resource execute(HttpServletRequest request, HttpServletResponse response,ServletFileUpload servletFileUpload) {
		UploadFiles uploadFiles = UploadFiles.tryBuild(request, servletFileUpload);
		//设置方法参数
		Object[] parameter = new Object[types.length];
		for(int i=0;i<types.length;i++){
			if(HttpServletRequest.class.equals(types[i])){
				parameter[i] = request;
			}else if(HttpServletResponse.class.equals(types[i])){
				parameter[i] = response;
			}else if(HttpSession.class.equals(types[i])){
				parameter[i] = request.getSession(true);
			}else if(UploadFiles.class.equals(types[i])){
				if(uploadFiles == null) {
					uploadFiles = new UploadFiles(request,servletFileUpload);
				}
				parameter[i] = uploadFiles;
			}
			//TODO 已废弃，未来版本不在支持
			else if(FileItem.class.equals(types[i])){
				if(uploadFiles != null) {
					parameter[i] = uploadFiles.getFirstFile();
				}
			}else if(ServletFileUpload.class.equals(types[i])){
				parameter[i] = servletFileUpload;
			}
		}
		Resource resource = null;
		try {
			Object result = this.method.invoke(obj,parameter);
			//如果不返回值则认为方法内部处理了响应对象
			if(result != null && !void.class.equals(result)) {
				if(String.class.isInstance(result)){
					//返回值为字符串
					resource = HandleByString(result.toString(),request,response);
				}else if(Resource.class.isInstance(result)) {
					resource = Resource.class.cast(result);
				}else {
					//如果是对象则按JSON处理
					resource = new JsonResource(result);
				}
			}
		} catch (Exception e) {
			//505错误
			log.error(e.getMessage(), e);
			resource = new ErrorResource(505);
		}
		
		return resource;
	}
	
	private Resource HandleByString(String result,HttpServletRequest request, HttpServletResponse response) throws Exception{
		Resource resource = null;
		if(StringUtils.startsWith(result, "redirect:")){
			String path = StringUtils.replaceOnce(result.toString(), "redirect:", "");
			response.sendRedirect(request.getContextPath() + path);
			resource = new VoidResource();
		}else if(StringUtils.startsWith(result,"text:")){
			String value = StringUtils.replaceOnce(result.toString(), "text:", "");
			String mediaType = "text/*";
			if(route.produces().length > 0){
				mediaType = route.produces()[0];
			}
			resource = new TextResource(mediaType,value);
		}else if(StringUtils.startsWith(result,"xml:")){
			String value = StringUtils.replaceOnce(result.toString(), "xml:", "");
			resource = new TextResource("text/xml",value);
		}else if(StringUtils.startsWith(result,"velocity:")){
			String value = StringUtils.replaceOnce(result.toString(), "velocity:", "");
			resource = new VelocityContentResource(value);
		}else{
			resource = new VelocityResource(result);
		}
		return resource;
	}

	public HttpHandler getNextHandler() {
		return nextHandler;
	}

	public void setNextHandler(HttpHandler nextHandler) {
		if(this.nextHandler != null){
			this.nextHandler.setNextHandler(nextHandler);
		}else{
			this.nextHandler = nextHandler;
		}
	}
	
	public String toString(){
		if(this.patterns != null){
			StringBuffer sb = new StringBuffer();
			for(Pattern pattern : this.patterns){
				sb.append(","+pattern.pattern());
			}
			if(sb.length() > 0){
				return sb.substring(1);
			}
			return sb.toString();
		}
		return super.toString();
	}
}
